package de.nak.fireDepartment.service;

import java.util.Collection;
import java.util.LinkedList;

import org.apache.log4j.Logger;
import org.springframework.dao.DataIntegrityViolationException;

import de.nak.fireDepartment.dao.CarDAO;
import de.nak.fireDepartment.exceptions.CarAlreadyExistsException;
import de.nak.fireDepartment.exceptions.CarNotFoundException;
import de.nak.fireDepartment.exceptions.EquipmentNotFoundException;
import de.nak.fireDepartment.exceptions.ErrorOnDeleteException;
import de.nak.fireDepartment.logger.MyLog4JLogger;
import de.nak.fireDepartment.model.Car;
import de.nak.fireDepartment.model.Department;
import de.nak.fireDepartment.model.Equipment;

/**
 * The car service implementation.
 *
 * @author Nils Bredow
 */
public class CarServiceImpl implements CarService {
	
	private Logger logger;
	
	/**
	 * Constructor gets the Logger an creates a message
	 */
	public CarServiceImpl(){
		logger = MyLog4JLogger.getLogger();
		logger.info("Instance of CarServiceImpl has been created");
	}
	
	/**
	 * The car data access object.
	 */
	private CarDAO carDAO;
	/**
	 * the equipmentService
	 */
	private EquipmentService equipmentService;
	
	/** {@inheritDoc}  */
	public void createCar(String modelName,
						  String yearOfManufacture,
						  int enginePerformance,
						  int engineSize,
						  String manufactureCompany,
						  int measurementsLength,
						  int measurementsHigh,
						  int measurementsWidth,
						  int crew,
						  String descriptionText,
						  String imageURL,
						  Long[] equipmentIds) throws CarAlreadyExistsException, EquipmentNotFoundException {
		
		// Create a new Car-Object
		logger.info("Create car-Object");
		Car car = new Car();
		// Store values in new Car Object
		car.setModelName(modelName);
		car.setYearOfManufacture(yearOfManufacture);
		car.setEnginePerformance(enginePerformance);
		car.setEngineSize(engineSize);
		car.setManufactureCompany(manufactureCompany);
		car.setMeasurementsHigh(measurementsHigh);
		car.setMeasurementsLength(measurementsLength);
		car.setMeasurementsWidth(measurementsWidth);
		car.setCrew(crew);
		car.setDescriptionText(descriptionText);
		car.setImageURL(imageURL);
		
		// initiate List for Equipments for the Car
		Collection<Equipment> equipments = new LinkedList<Equipment>();
		
		// loop over the array of EquipmentIds and add the equipments to the List
		logger.info("Get selected Equipments for the Car");
		if(equipmentIds != null){
			for(int i = 0; i < equipmentIds.length; i++){
				Long equipId = equipmentIds[i];
				if(equipId != null && equipId > 0){
					Equipment equipment = equipmentService.showEquipment(equipId);
					equipments.add(equipment);		
				}
			}
		}
		// store equipmentlist into the car
		car.setEquipments(equipments);
		
		logger.info("Car has been created");
		try {
			// check for existing Car
			checkCarExists(car);
			// save Car
			carDAO.save(car);
			logger.info("Car has been saved");
		}
		catch (DataIntegrityViolationException ex) {
			logger.warn(ex.getStackTrace());
			throw new CarAlreadyExistsException();
		}
	}

	/** {@inheritDoc} */
	public Collection<Car> listCars() {
		logger.info("All Cars should be list off");
		return carDAO.findAll();
	}

	/** {@inheritDoc} */
	public Car showCar(Long id) throws CarNotFoundException {
		logger.info("A Car should be searched for");
		Car car = carDAO.findById(id);
		if (car == null) {
			logger.warn("Car not found");
			throw new CarNotFoundException();
		}
		logger.info("Searched Car found");
		
		// Convert PersistentCarObjects to CarList
		logger.info("Get departments of the car");
		Collection<Department> departments = new LinkedList<Department>();
		for (Department dept : car.getDepartments()) {
			departments.add(dept);
		}
		car.setDepartments(departments);
		
		// Convert PersistentEquipmentObjects to EquipmentList
		logger.info("Get the equipments of the car");
		Collection<Equipment> equipments = new LinkedList<Equipment>();
		for (Equipment equip : car.getEquipments()) {
			equipments.add(equip);
		}
		car.setEquipments(equipments);
		
		return car;
	}

	/** {@inheritDoc} */
	public void updateCar(long id,
						  String modelName,
						  String yearOfManufacture,
						  int enginePerformance,
						  int engineSize,
						  String manufactureCompany,
						  int measurementsLength,
						  int measurementsHigh,
						  int measurementsWidth,
						  int crew,
						  String descriptionText,
						  String imageURL,
						  Long[] equipmentIds) throws CarNotFoundException, EquipmentNotFoundException, CarAlreadyExistsException {
		
		logger.info("A Car should be updated");
		Car car = carDAO.findById(id);
		if (car == null) {
			logger.warn("Car not found");
			throw new CarNotFoundException();
		}
		logger.info("Searched Car found");
		
		// check for existing Car
		checkCarExists(car);
		
		car.setModelName(modelName);
		car.setYearOfManufacture(yearOfManufacture);
		car.setEnginePerformance(enginePerformance);
		car.setEngineSize(engineSize);
		car.setManufactureCompany(manufactureCompany);
		car.setMeasurementsHigh(measurementsHigh);
		car.setMeasurementsLength(measurementsLength);
		car.setMeasurementsWidth(measurementsWidth);
		car.setCrew(crew);
		car.setDescriptionText(descriptionText);
		
		// initiate List for Equipments for the Car
		Collection<Equipment> equipments = new LinkedList<Equipment>();
		
		// loop over the array of EquipmentIds and add the equipments to the List
		logger.info("Get selected Equipments for the Car");
		if(equipmentIds != null){
			for(int i = 0; i < equipmentIds.length; i++){
				Long equipId = equipmentIds[i];
				if(equipId != null && equipId > 0){
					Equipment equipment = equipmentService.showEquipment(equipId);
					equipments.add(equipment);		
				}
			}
		}
		car.setEquipments(equipments);
		
		if(!imageURL.equals("")){
			car.setImageURL(imageURL);
		}
		
		logger.info("Car has been updated");
	}

	/** {@inheritDoc} 
	 * @throws ErrorOnDeleteException */
	public void deleteCar(Long id) throws CarNotFoundException, ErrorOnDeleteException {
		logger.info("A Car should be updated");
		// get Car
		Car car = carDAO.findById(id);
		if (car == null) {
			logger.warn("Car not found");
			throw new CarNotFoundException();
		}
		
		// If car is linked by departments it can't be deleted.
		if(car.getDepartments().size() == 0){
			carDAO.delete(car);
			logger.info("Car has been deleted");
		}else{
			logger.info("Car can't be deleted due to refertial Integrity");
			throw new ErrorOnDeleteException("text.error.referentialIntegrity.car");
		}
	}
	
	/**
	 * Check if a Car is alreadyExisting
	 * @param car
	 * @throws CarAlreadyExistsException
	 */
	private void checkCarExists(Car car) throws CarAlreadyExistsException{
		Collection<Car> cars = listCars();
		// iterate over all cars
		for (Car aCar : cars) {
			// if modelname == modelname and id != id throw Exception
			if(car.getModelName().equals(aCar.getModelName()) && car.getCarId() != aCar.getCarId()){
				logger.info("Car already existing");
				throw new CarAlreadyExistsException("text.error.carExists");
			}
		}
	}

	/**
	 * Returns the car DAO.
	 * @return the carDAO.
	 */
	public CarDAO getCarDAO() {
		return carDAO;
	}

	/**
	 * Sets the car DAO.
	 * @param carDAO the carDAO to set.
	 */
	public void setCarDAO(CarDAO carDAO) {
		this.carDAO = carDAO;
	}

	/**
	 * Gets the EquipmentService
	 * @return the equipmentService
	 */
	public EquipmentService getEquipmentService() {
		return equipmentService;
	}

	/**
	 * Sets the EquipmentSetvice
	 * @param equipmentService the equipmentService to set
	 */
	public void setEquipmentService(EquipmentService equipmentService) {
		this.equipmentService = equipmentService;
	}	
	
}
